----------------------------------------------------------------------------------
-- Company: 			StrathSat-R
-- Engineer: 			Thomas Parry
-- 
-- Create Date:    	19:12:51 07/25/2012 
-- Design Name: 		Command transmitter
-- Module Name:    	cmd_tx - behavioral 
-- Project Name: 		FPGA data storage
-- Target Devices: 	Spartan 6
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity cmd_tx is
	generic
	(
		CMD_LENGTH			:	integer := 6;
		ARGUMENT_LENGTH	:	integer := 32;
		CRC_LENGTH			:	integer := 7
	);
   port
	( 
		Clk 		: in  std_logic;
      Strobe 	: in  std_logic;
      Cmd 		: in  std_logic_vector(CMD_LENGTH-1 downto 0);
      Argument : in  std_logic_vector(ARGUMENT_LENGTH-1 downto 0);
      Output 	: out std_logic;
      Done 		: out std_logic
	);
end cmd_tx;

architecture behavioral of cmd_tx is

	---- constants
	constant MAIN_LENGTH	:	integer := CMD_LENGTH + ARGUMENT_LENGTH + 2;
	constant START_BITS	:	std_logic_vector(1 downto 0) := 	"01";
	constant END_BIT		:	std_logic := '1';

	---- signals 
	-- counter
	signal count 		:	integer range 0 to MAIN_LENGTH+CRC_LENGTH;
	
	-- shift register signals
	signal cmd_shift		: 	std_logic_vector(MAIN_LENGTH-1 downto 0);
	signal crc_shift		: 	std_logic_vector(CRC_LENGTH-1 downto 0);
	
	-- state flags
	signal crc_en			: 	std_logic;
	signal count_en		:	std_logic;
	

	-- main state machine type
	type state_type is
	(
		idle,
		command,
		crc,
		last_bit,
		finished
	);
	
	signal state : state_type;

begin

	---------------------------------------------------------------------------------------
	-- main state machine
	--		clocked process
	SM_CLKD : process(Clk)
	begin
	
		if rising_edge(Clk) then
			
			case state is
			
				when idle => 		if Strobe = '1' then
											state <= command;
										else
											state <= idle;
										end if;
				
				---------------------------------------------------------------------
				
				when command =>	if count = MAIN_LENGTH-1 then
											state <= crc;
										else
											state <= command;
										end if;
				
				---------------------------------------------------------------------
				
				when crc =>			if count = MAIN_LENGTH+CRC_LENGTH-1 then
											state <= last_bit;
										else
											state <= crc;
										end if;
										
				---------------------------------------------------------------------
				
				when last_bit =>	state <= finished;
				
				---------------------------------------------------------------------
				
				when finished =>	state <= idle;
				
				---------------------------------------------------------------------
				
				when others =>		state <= idle;
				
			end case;
		end if;
		
	end process SM_CLKD;
	
	---------------------------------------------------------------------------------------
	-- main state machine
	--		combinational process
	SM_CMB : process(state)
	begin
		
		-- default values
		crc_en 	<= '0';
		count_en	<=	'0';
		Done		<= '0';
	
		case state is
			
				when idle => 
				---------------------------------------------------------------------
				when command =>	count_en	<= '1';
										crc_en	<=	'1';
				---------------------------------------------------------------------
				when crc =>			count_en	<= '1';
				---------------------------------------------------------------------
				when last_bit =>	
				---------------------------------------------------------------------
				when finished =>	Done 		<= '1';
				---------------------------------------------------------------------
				when others =>
				
			end case;
			
	end process SM_CMB;
	
	---------------------------------------------------------------------------------------
	-- Counting process
	
	COUNT_PROC	: process (Clk)
	begin
		
		if rising_edge(Clk) then
			
			if count_en = '0' then
				count <= 0;
			else
				count <= count + 1;
			end if;
			
		end if;
	end process COUNT_PROC;
	
	---------------------------------------------------------------------------------------
	-- Command shift register 
	
	COMMAND_SHIFT : process(Clk)
	begin
	
		if rising_edge(Clk) then
			
			if count_en = '1' then
				cmd_shift(MAIN_LENGTH-1 downto 1) <= cmd_shift(MAIN_LENGTH-2 downto 0);
				cmd_shift(0) <= '0';
			else
				cmd_shift <= START_BITS & CMD & ARGUMENT;
			end if;
			
		end if;
	end process COMMAND_SHIFT;
	
	---------------------------------------------------------------------------------------
	-- CRC calculator
	
	CRC7 : if CRC_LENGTH = 7 generate
	
		CRC_CALC : process(Clk)
		begin
	
			if rising_edge(Clk) then
			
				if crc_en = '0' then
					crc_shift <= crc_shift(5 downto 0) & '0';
				else		
					crc_shift(6 downto 4) <= crc_shift(5 downto 3);
					crc_shift(3) <= cmd_shift(MAIN_LENGTH-1) xor crc_shift(6) xor crc_shift(2);
					crc_shift(2 downto 1) <= crc_shift(1 downto 0);
					crc_shift(0) <= cmd_shift(MAIN_LENGTH-1) xor crc_shift(6);
				end if;
			end if;
			
		end process CRC_CALC;
	end generate CRC7;
	
	---------------------------------------------------------------------------------------
	-- output MUX
	
	-- select output depending on state
	with state select
		Output <=	'Z'								when idle,
						cmd_shift(MAIN_LENGTH-1)	when command,
						crc_shift(CRC_LENGTH-1)		when crc,
						END_BIT							when last_bit,
						'Z'								when others;
		

end behavioral;

